#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>

#include "Socket.h"

jint Java_my_socket_Native_socket(JNIEnv* env, jclass clazz) {
	// AF_INET表示使用IPv4 SOCK_STREAM表示stream协议 0表示TCP
	// SOCK_NONBLOCK：在新打开的文件描述中设置O_NONBLOCK文件状态标志。使用此标志可以节省对fcntl(2)的额外调用，以获得相同的结果。
	// 在Linux2.6.27之前等同于：
	//      int socketFd = socket(AF_INET, SOCK_STREAM, 0);
	//      int flags = fcntl(socketFd, F_GETFL, 0));
	//      int fcntl(socketFd, F_SETFL, flags | O_NONBLOCK);
    return socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
}

jint Java_my_socket_Native_bind(JNIEnv* env, jclass clazz, jint fd, jint port) {
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(port);
    if (bind(fd, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) {
		return -errno;
    }
	return 0;
}

jint Java_my_socket_Native_listen(JNIEnv* env, jclass clazz, jint fd, jint backlog) {
	// backlog决定了内核的连接缓存队列长度，对于一个给定的监听套接字，内核维护两个队列：
	// 1. 未就绪队列，存放没有完成三路握手的连接，监听套接字收到SYN并返回ACK+SYN，连接处于SYN_RECV状态，等待对端发送ACK。
	// 如果已完成队列非满，则接收ACK，连接握手完成，进入已完成队列；如果已完成队列满则丢弃ACK，对端重发ACK（对端看到的连接是ESTABLISED状态），
	// 若未就绪队列中的SYN_RECV等待直到超时还没进入已完成队列则丢弃连接（对端不知道，只有在读写套接字时才知道）。
	// 2. 已完成队列，存放已经完成三路握手的连接（ESTABLISHED），等待accept取走连接。
	// backlog决定了两个队列的长度之和（并不是说两个队列之和等于backlog，而是存在个转换，依赖于具体实现）。
    if (listen(fd, backlog) == -1) {
		return -errno;
	}
	return 0;
}

jint Java_my_socket_Native_accept(JNIEnv* env, jclass clazz, jint fd) {
    struct sockaddr_in client_addr;
	socklen_t client_addr_length;
	jint socketFd;
	int err;

	client_addr_length = sizeof(client_addr);
	for (;;) {
	    // 在Linux2.6.28之前等同于：
	    //      socketFd = accept(fd, (struct sockaddr*) &client_addr, &client_addr_length);
	    //      int flag = fcntl(client_sockfd, F_GETFL);
	    //      flag |= O_NONBLOCK;
	    //      fcntl(client_sockfd, F_SETFL, flag);
		socketFd = accept4(fd, (struct sockaddr*) &client_addr, &client_addr_length, SOCK_NONBLOCK | SOCK_CLOEXEC);
		if (socketFd != -1) {
			return socketFd;	
		}
		// 慢系统调用指的是可能永远阻塞的系统调用。永远阻塞的系统调用是指调用永远无法返回，多数网络支持函数都属于这一类。
		// 如果进程在一个慢系统调用(slow system call)中阻塞时，当捕获到某个信号且相应信号处理函数返回时，这个系统调用被中断，
		// 调用返回错误，设置errno为EINTR（相应的错误描述为“Interrupted system call”）。
		if ((err = errno) != EINTR) {
			return -err;
		}
	}
}